package com.ivan.toolkit.common;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.security.Key;
import java.security.SecureRandom;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

import com.google.common.io.Files;

public class FileUtilities {
	

	
	public static class CipherAndZip {
		
		public static File wrap(File file, String privateKey) throws GeneralSecurityException, IOException {
			
			if(file == null || !file.exists())
				return null;
			
			File zip = new File(file.getCanonicalPath() + ".zip");
			
			Zips.zip(file.getCanonicalPath(), zip.getCanonicalPath());
			
			File wrap = new File(file.getCanonicalPath() + ".wrap");
			
			Ciphers.encrypt(zip.getCanonicalPath(), wrap.getCanonicalPath(), privateKey);
			
			zip.delete();
			
			return wrap;
			
		}
		
		public static File wrap(File file) throws GeneralSecurityException, IOException {
			
			return wrap(file, Securitys.privateKey());
			
		}
		
		public static File unwrap(File file, String privateKey) throws GeneralSecurityException, IOException {
			
			
			
			if(file == null || !file.exists())
				return null;
			
			if(file.isDirectory()) {
				
				System.out.println("Unwrap dir: " + file.getAbsolutePath());
				
				File[] files = file.listFiles((f, s)->{
					return s.endsWith(".wrap");
				});
				for(File f : files) {
					unwrap(f, privateKey);	
					Sleeps.sleepQuietly(10);
				}
				return file;
			}
			
			System.out.println("Unwrap file: " + file.getAbsolutePath());
			
			File zip = new File(file.getCanonicalPath() + ".zip");
			if(zip.exists()) return null;
			
			File unwrap = null;
			/*
			if(file.getCanonicalPath().endsWith(".wrap")) {
				unwrap = new File(file.getCanonicalPath().replaceFirst(".wrap", ""));
			} else {
				unwrap = new File(file.getCanonicalPath() + ".unwrap");
			}
			*/
			unwrap = new File(file.getCanonicalPath() + ".unwrap");
			
			if(unwrap.exists()) return null;
			
			System.out.println(" => Cipher... ");
			Ciphers.decrypt(file.getCanonicalPath(), zip.getCanonicalPath(), privateKey);

			unwrap.mkdirs();
			
			System.out.println(" => Unzip... ");
			Zips.unzip(zip.getCanonicalPath(), unwrap.getCanonicalPath());

			System.out.println(" => Delete zip file... ");
			zip.delete();
			
			// 如果解包后，文件夹内只有一个文件，则将其移动到源文件目录，同时删除生成的解包目录
			if(unwrap.isDirectory()) {
				
				File[] files = unwrap.listFiles();
				if(files.length == 1) {
					
					System.out.println(" => Pull file outof dir");
					
					File target = new File(unwrap.getParentFile(), files[0].getName());
					Files.move(files[0], target);
					
					unwrap.delete();
					unwrap = target;
				}
			}
			
			System.out.println(" => Finish");
			
			return unwrap;
			
		}
		
		public static File unwrap(File file) throws GeneralSecurityException, IOException {
			
			return unwrap(file, Securitys.privateKey());
			
		}
		
		
	}

	
	public static class Ciphers {
		/**   
	     * 使用AES对文件进行加密和解密   
	     *    
	     */    
	    private static String type = "AES";     
	    
	    /**   
	     * 把文件srcFile加密后存储为destFile   
	     * @param srcFile     加密前的文件   
	     * @param destFile    加密后的文件   
	     * @param privateKey  密钥   
	     * @throws GeneralSecurityException   
	     * @throws IOException   
	     */    
	    public static void encrypt(String srcFile, String destFile, String privateKey) throws GeneralSecurityException, IOException {     
	        Key key = getKey(privateKey);     
	        Cipher cipher = Cipher.getInstance(type);     
	        cipher.init(Cipher.ENCRYPT_MODE, key);     
	    
	        FileInputStream fis = null;     
	        FileOutputStream fos = null;     
	        try {     
	            fis = new FileInputStream(srcFile);     
	            fos = new FileOutputStream(mkdirFiles(destFile));     
	    
	            crypt(fis, fos, cipher);     
	            
	            fos.flush();
	            
	        } catch (FileNotFoundException e) {     
	            e.printStackTrace();     
	        } catch (IOException e) {     
	            e.printStackTrace();     
	        } finally {     
	            if (fis != null) {     
	                fis.close();     
	            }     
	            if (fos != null) {     
	                fos.close();     
	            }     
	        }     
	    }     
	    
	    /**   
	     * 把文件srcFile解密后存储为destFile   
	     * @param srcFile     解密前的文件   
	     * @param destFile    解密后的文件   
	     * @param privateKey  密钥   
	     * @throws GeneralSecurityException   
	     * @throws IOException   
	     */    
	    public static void decrypt(String srcFile, String destFile, String privateKey) throws GeneralSecurityException, IOException {     
	        Key key = getKey(privateKey);     
	        Cipher cipher = Cipher.getInstance(type);     
	        cipher.init(Cipher.DECRYPT_MODE, key);     
	    
	        FileInputStream fis = null;     
	        FileOutputStream fos = null;     
	        try {     
	            fis = new FileInputStream(srcFile);     
	            fos = new FileOutputStream(mkdirFiles(destFile));     
	    
	            crypt(fis, fos, cipher);
	            
	            fos.flush();
	            
	        } catch (FileNotFoundException e) {     
	            e.printStackTrace();     
	        } catch (IOException e) {     
	            e.printStackTrace();     
	        } finally {     
	            if (fis != null) {     
	                fis.close();     
	            }     
	            if (fos != null) {     
	                fos.close();     
	            }     
	        }     
	    }     
	    
	    /**   
	     * 根据filePath创建相应的目录   
	     * @param filePath      要创建的文件路经   
	     * @return  file        文件   
	     * @throws IOException   
	     */    
	    private static File mkdirFiles(String filePath) throws IOException {     
	        File file = new File(filePath);     
	        if (!file.getParentFile().exists()) {     
	            file.getParentFile().mkdirs();     
	        }     
	        file.createNewFile();     
	    
	        return file;     
	    }     
	    
	    /**   
	     * 生成指定字符串的密钥   
	     * @param secret        要生成密钥的字符串   
	     * @return secretKey    生成后的密钥   
	     * @throws GeneralSecurityException   
	     */    
	    private static Key getKey(String secret) throws GeneralSecurityException {     
	    	/*
	        KeyGenerator kgen = KeyGenerator.getInstance(type);     
	        kgen.init(128, new SecureRandom(secret.getBytes()));     
	        SecretKey secretKey = kgen.generateKey();     
	        return secretKey;     
	        */
	    	
	        KeyGenerator kgen = KeyGenerator.getInstance(type); 
	        SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG");
            secureRandom.setSeed(secret.getBytes());
            kgen.init(128, secureRandom);
            SecretKey secretKey = kgen.generateKey();
            byte[] enCodeFormat = secretKey.getEncoded();
            SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
            return key;
	        
	    }     
	    
	    /**   
	     * 加密解密流   
	     * @param in        加密解密前的流   
	     * @param out       加密解密后的流   
	     * @param cipher    加密解密   
	     * @throws IOException   
	     * @throws GeneralSecurityException   
	     */    
	    private static void crypt(InputStream in, OutputStream out, Cipher cipher) throws IOException, GeneralSecurityException {     
	        int blockSize = cipher.getBlockSize() * 1000;     
	        int outputSize = cipher.getOutputSize(blockSize);     
	    
	        byte[] inBytes = new byte[blockSize];     
	        byte[] outBytes = new byte[outputSize];     
	    
	        int inLength = 0;     
	        boolean more = true;     
	        while (more) {     
	            inLength = in.read(inBytes);     
	            if (inLength == blockSize) {     
	                int outLength = cipher.update(inBytes, 0, blockSize, outBytes);     
	                out.write(outBytes, 0, outLength);     
	            } else {     
	                more = false;     
	            }     
	        }     
	        if (inLength > 0)     
	            outBytes = cipher.doFinal(inBytes, 0, inLength);     
	        else    
	            outBytes = cipher.doFinal();     
	        out.write(outBytes);     
	    }     
	}

	
	public static class Zips {
		
	    /**得到当前系统的分隔符*/    
		//  private static String separator = System.getProperty("file.separator");     
	    
	    /**   
	     * 添加到压缩文件中   
	     * @param out   
	     * @param f   
	     * @param base   
	     * @throws Exception   
	     */    
	    private static void directoryZip(ZipOutputStream out, File f, String base) throws Exception {     
	        // 如果传入的是目录     
	        if (f.isDirectory()) {     
	            File[] fl = f.listFiles();     
	            // 创建压缩的子目录     
	            out.putNextEntry(new ZipEntry(base + "/"));     
	            if (base.length() == 0) {     
	                base = "";     
	            } else {     
	                base = base + "/";     
	            }     
	            for (int i = 0; i < fl.length; i++) {     
	                directoryZip(out, fl[i], base + fl[i].getName());     
	            }     
	        } else {     
	            // 把压缩文件加入rar中     
	            out.putNextEntry(new ZipEntry(base));     
	            FileInputStream in = new FileInputStream(f);     
	            byte[] bb = new byte[10240];     
	            int aa = 0;     
	            while ((aa = in.read(bb)) != -1) {     
	                out.write(bb, 0, aa);     
	            }     
	            in.close();     
	        }     
	    }     
	    
	    /**   
	     * 压缩文件   
	     *    
	     * @param zos   
	     * @param file   
	     * @throws Exception   
	     */    
	    private static void fileZip(ZipOutputStream zos, File file) throws Exception {     
	        if (file.isFile()) {     
	            zos.putNextEntry(new ZipEntry(file.getName()));     
	            FileInputStream fis = new FileInputStream(file);     
	            byte[] bb = new byte[10240];     
	            int aa = 0;     
	            while ((aa = fis.read(bb)) != -1) {     
	                zos.write(bb, 0, aa);     
	            }
	            fis.close();     
	            System.out.println(file.getName());     
	        } else {     
	            directoryZip(zos, file, "");     
	        }     
	    }     
	    
	    /**   
	     * 解压缩文件   
	     *    
	     * @param zis   
	     * @param file   
	     * @throws Exception   
	     */    
	    private static void fileUnZip(ZipInputStream zis, File file) throws Exception {     
	        ZipEntry zip = zis.getNextEntry();     
	        if (zip == null)     
	            return;     
	        String name = zip.getName();     
	        File f = new File(file.getAbsolutePath() + "/" + name);     
	        if (zip.isDirectory()) {     
	            f.mkdirs();     
	            fileUnZip(zis, file);     
	        } else {     
	            f.createNewFile();     
	            FileOutputStream fos = new FileOutputStream(f); 
	            try {
		            byte b[] = new byte[10240];     
		            int aa = 0;     
		            while ((aa = zis.read(b)) != -1) {     
		                fos.write(b, 0, aa);     
		            }
	            } catch(Exception e) {
	            	
	            } finally {
	            	fos.close();
	            }
	            fileUnZip(zis, file);     
	        }     
	    }     
	         
	    /**   
	     * 根据filePath创建相应的目录   
	     * @param filePath   
	     * @return   
	     * @throws IOException   
	     */    
	    private static File mkdirFiles(String filePath) throws IOException{     
	        File file = new File(filePath);     
	        if(!file.getParentFile().exists()){     
	            file.getParentFile().mkdirs();     
	        }     
	        file.createNewFile();     
	             
	        return file;     
	    }     
	    
	    /**   
	     * 对zipBeforeFile目录下的文件压缩，保存为指定的文件zipAfterFile   
	     *    
	     * @param zipBeforeFile     压缩之前的文件   
	     * @param zipAfterFile      压缩之后的文件   
	     */    
	    public static void zip(String zipBeforeFile, String zipAfterFile) {     
	        try(ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(mkdirFiles(zipAfterFile)))){      
	            fileZip(zos, new File(zipBeforeFile));   
	            zos.flush();  
	        } catch (Exception e) {     
	            e.printStackTrace();     
	        }     
	    }     
	    
	    /**   
	     * 解压缩文件unZipBeforeFile保存在unZipAfterFile目录下   
	     *    
	     * @param unZipBeforeFile       解压之前的文件   
	     * @param unZipAfterFile        解压之后的文件   
	     */    
	    public static void unzip(String unZipBeforeFile, String unZipAfterFile) {     
	        try(ZipInputStream zis = new ZipInputStream(new FileInputStream(unZipBeforeFile))) {          
	            File f = new File(unZipAfterFile);     
	            f.mkdirs();     
	            fileUnZip(zis, f);  
	        } catch (Exception e) {     
	            e.printStackTrace();     
	        }     
	    }     
	}

}
